Skip to content

RK3326: add GKD Pixel 2 (Device b) support#2993

Merged
stolen merged 1 commit into
ROCKNIX:nextfrom
Jacob-Matthew-Cook:gkd-pixel2-support
Jul 17, 2026
Merged

RK3326: add GKD Pixel 2 (Device b) support#2993
stolen merged 1 commit into
ROCKNIX:nextfrom
Jacob-Matthew-Cook:gkd-pixel2-support

Conversation

@Jacob-Matthew-Cook

@Jacob-Matthew-Cook Jacob-Matthew-Cook commented Jul 7, 2026

Copy link
Copy Markdown

Summary

  • Adds full boot/display/input support for the GKD Pixel 2 handheld (RK3326S), merged into RK3326 subdevice b.
  • PX30S SoC-variant kernel patches: ports the official Rockchip vendor commits for this silicon variant (TSADC, OTP, GPU power-domain, DSI DPHY timing, USB2 PHY tuning, io-domain) onto a shared soc_is_px30s() detection helper ported from the vendor's own cpuinfo infrastructure.
  • Black-screen root cause and fix: the panel is a genuine Sitronix ST7703-family part, compatible as well with sitronix,st7701 in the vendor tree if using a non-generic driver. Its DSI reset-gpios polarity was also wrong (active-low instead of active-high), so the panel was being told to exit reset every boot and instead held in it the whole time - it never processed a single DSI command, which is why the backlight came on but the screen stayed black. Adds a proper gkd,pixel2-panel entry to mainline's panel-sitronix-st7701.c with correct reset/enable GPIO sequencing for this IC family.
  • Input: EmulationStation (es_input.cfg) and RetroArch autoconfig entries (previously missing entirely for this board), an A/B button swap fix, and GPIO battery-status LED support (4 green + 1 yellow, percentage-tiered).
  • Bootloader: simplified to ROCKNIX's own self-contained SPL+BL31+trust+U-Boot blob (dropped an earlier vendor-idbloader hybrid once this blob was confirmed to correctly train the board's LPDDR4 RAM).
  • USB OTG host-mode detection, backlight minimum-brightness floor correction.

Testing

  • Built and tested on real GKD Pixel 2 hardware.
  • Confirmed working: boot, display (correct orientation, no PX30S-specific artifacts), touch/button input (D-pad, face buttons, shoulders/triggers, select/start/menu), battery status LEDs, USB OTG host mode.
  • Former issues, NOW FIXED (mostly:
    • Rumble motor state: the PWM channel powers up already-enabled/inverted/duty==period, which an active-low motor driver reads as permanently "on". Confirmed via force-feedback testing that it can be stopped once forced into a real driver-verified state - only the very first transition away from the exact boot-inherited register state fails. Two fixes tried and disproven on hardware so far (PWM core stale-state strobe, missing PWM_LOCK_EN hold time).
    • Sleep/wake: device suspends correctly but the RAM is prone to corruption following resume. This occurs primarily during CPU clock speed/voltage transitions.

AI Usage

Did you use AI tools to help write this code? YES, Claude. Most code has been adapted from BSP sources and the recommended vendor DTS settings.

@stolen

stolen commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

This looks like too large modification for supporting just one device.
Hard to verify where LLM does something useful and where just doesn't try specify needed stuff in dts.

@stolen

stolen commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Kernel patches look like they are implemented by LLM.
Meanwhile BSP kernel 6.1 has its own px30s patches
https://github.com/rockchip-linux/kernel

~/px30s/kernel$ git l1 | grep -Ei '(px30s|rk3326s)'
| * | | | | | ac35846c24ff Zhihuan He   arm64: dts: rockchip: px30s: reduce LP4 overshoot when ODT off
| * | | | | | b6de31a02254 Guochun Hu.. phy/rockchip: inno-dsidphy: add support px30s
| * | | | | | 480163a87a56 Zhen Chen    soc: rockchip: power-domain: correct name of pd_gpu for px30s
| * | | | | | 28bf3a52e49f Jianqun Xu   arm64: dts: rockchip: px30 set io drive-strength-s for px30s
| * | | | | | 711167d7b0b2 Jianwei Zh.. phy: rockchip: inno-usb2: support rk3326s and px30s tuning
| * | | | | | 41f9bdf3cf40 Liang Chen   soc: rockchip: power-domain: keep pd_gpu always on for px30s
| * | | | | | 0a724e69ebb3 Zefa Chen    phy: rockchip: mipi-rx: support rk3326s mipi dphy rx
| * | | | | | 779017748410 Jianqun Xu   power/avs: rockchip-io-domain: px30s not support pmuio1 1v8 mode
| * | | | | | 3724dcef5f0c Elaine Zhang clk: rockchip: px30: support px30s
| * | | | | | f4d06e8002ea Elaine Zhang arm64: dts: rockchip: px30s: add scmi/opp-table/dmc_fsp node
| * | | | | | | | | | bc7c0c8e19f4 Finley Xiao  thermal: rockchip: Support the px30s SoC in thermal driver
| * | | | | | | | | | c106a565f611 Jianqun Xu   pinctrl: rockchip: fix drive strength set for rk3308bs/px30s/rk3326s
| * | | | | | | | | | fd7fa23ef959 Finley Xiao  nvmem: rockchip-otp: Add support for px30s otp
| * | | | | | | | | | 84d26633c7a4 Liang Chen   soc: rockchip: add cpuid for px30/px30s

Why not port these patches to mainline?

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

I'll can trim down and remove the unnecessary source edits, as well as any excess patch files. I have been referencing the px30s source code from the BSP 6.1 when making these edits, but we can definitely import these driver files outright. That is a better idea.

@AveyondFly

AveyondFly commented Jul 9, 2026

Copy link
Copy Markdown

I'll can trim down and remove the unnecessary source edits, as well as any excess patch files. I have been referencing the px30s source code from the BSP 6.1 when making these edits, but we can definitely import these driver files outright. That is a better idea.

Hi, in fact I also spent some time on RK3326s before with another device GameMT E6. In my fork, I can boot the 3326s device with bsp 6.6 kernel. And the basic funcionality works properly. The only leftover is after suspend, the device will not recognize the USB port and something wrong with devfreq.
I assume LLM just need to do the adaption for dts in my fork. I have add the RK3326S device.
https://github.com/AveyondFly/distribution_rocknix

update:
Switch to 5.10 BSP and add pxiel2 with cursor's composer2.5's help:
https://github.com/AveyondFly/distribution-nightly/releases/download/nightly-20260713/AURKNIX-RK3326S.aarch64-20260713.img.gz

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

If I am not mistaken, wouldn't the ROCKNIX dev's want the ROCKNIX kernel to be built and compatible with this device? Many other distributions (ROGUE OS, Knulli) leverage the BSP Kernel. While this is fine, I am not sure this is satisfactory for mainline ROCKNIX. The problems we are having stem directly from porting to the ROCKNIX kernel.

I can definitely look at your code as a reference and use it to help make an "RK3326S" device in the devices list.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

@stolen With the help of r3claimer we have the screen working, and have confirmed the Audio, USB, and Buttons are all working. I have also attempted to minimize any excess modification to the ROCKNIX source tree, and have imported the necessary PX30S patches from their BSP tree.

@r3claimer

r3claimer commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Here is the logic to add LED controlls to the dts. Luckily they are just basic GPIO.

leds {
	compatible = "gpio-leds";
	pinctrl-names = "default";
	pinctrl-0 = <&led_gpio>;

	led-0 {
		gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>;
		label = "battery0";
	};
	led-1 {
		gpios = <&gpio1 RK_PB4 GPIO_ACTIVE_HIGH>;
		label = "battery1";
	};
	led-2 {
		gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>;
		label = "battery2";
	};
	led-3 {
		gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>;
		label = "battery3";
	};
	led-4 {
		gpios = <&gpio1 RK_PB7 GPIO_ACTIVE_HIGH>;
		label = "battery4";
	};
};

&pinctrl {
	led {
		led_gpio: led-gpio {
			rockchip,pins =
				<1 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>,
				<1 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>,
				<1 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>,
				<1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>,
				<1 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
};

I believe they will show in /sys/class/leds/brightnessX. Just adding to the dts turns them all on by default. Echo just a 0 or a 1 should turn them on and off. Ideal would be to create a script that showed the remaining battery percentage relflect in number of leds lit up.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

We have enabled GPIO LED control for the GKD Pixel2. Added a gpio-leds node for the five battery-indicator LEDs (gpio1 RK_PB2/PB4/PB5/PB6/PB7), based on the GPIO wiring @r3claimer posted above. They're exposed under /sys/class/leds/batteryN/brightness.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Important: resume-from-sleep is still NOT working correctly on the GKD Pixel2. The device suspends fine but does not wake.

Investigation update: found the likely root cause via the vendor rockchip-linux/kernel tree - on PX30S, the deepest suspend modes are governed by ATF/BL31 firmware, not Linux. device_init_wakeup()/enable_irq_wake() only keep an IRQ line unmasked; firmware alone decides what wakes the CPU cores once they're powered off. Our board's devicetree already carries the correct rockchip,sleep-mode-config/rockchip,wakeup-config values (copied from the vendor tree), but nothing in this kernel ever parsed the rockchip,pm-px30 compatible string or sent those values to firmware.

Added 034-px30s-suspend-wakeup-config.patch, a trimmed port of the vendor's rockchip_pm_config.c that sends this config via the SIP_SUSPEND_MODE SMC call. It compiles cleanly against this kernel/config, but has not been confirmed working on real hardware - it depends on this board's BL31 firmware blob actually implementing that SIP call. If it doesn't, this is a silent no-op and wake-from-sleep remains broken. Needs real-hardware testing before this can be considered resolved.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Ready for alpha testing.

Summary of where things stand:

  • Display, audio, USB, buttons (A/B swap fixed), battery LED control, and FUNC-key hotkey are all working and confirmed on real hardware.
  • Battery/fuel-gauge reporting confirmed accurate after a one-time PMIC recalibration (documented, not yet automated as a quirk).
  • Input controls now wired up beyond RetroArch: gamecontrollerdb.txt (covers most standalone emulators/apps generically) plus explicit Flycast/Mupen64Plus configs for the two that need per-device sections.
  • Fixed a real bug in the shared RK3326 update.sh bootloader script: subdevice detection never resolved to "c", so a .tar/.img update would have flashed the wrong u-boot binary onto this board. Fixed with proper Pixel2 detection.
  • Known open issue: wake-from-sleep does not work. Two separate fix attempts (a kernel wake-irq patch, and a ported vendor SIP suspend-mode driver - confirmed this board's BL31 does implement the required SIP call) were both tested on real hardware with no change in behavior. Root cause still open. A temporary freeze (s2idle) workaround is in place so sleep/resume is at least usable in the meantime.

@stolen @r3claimer - given the volume of changes, would appreciate eyes on this whenever you have a chance, especially the wake-from-sleep investigation if either of you has PX30S/ATF firmware insight.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

GKD Pixel2 is now merged into Device B — subdevice "c" has been removed entirely.

Its u-boot/kernel/system payload was already identical to subdevice b's, so the only real difference was dtb selection. Since Pixel2 has no analog stick wired to the SARADC channel the existing ADC-hwid mechanism relies on (that pin floats and can land in any board's range on a given boot), b_boot.ini now detects it directly from the SoC's own silicon instead: PX30S vs plain PX30 differ in the DDR_GRF_CON1 register (bits 15:14 read 0b11 only on PX30S), checked before and taking priority over the ADC-based selection every other subdevice-b board uses.

Verified working on real hardware.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Alpha 1.0 - feature complete. 🎉

GKD Pixel2 support has since been merged into subdevice b (no longer a separate subdevice c) with hardware-silicon-register-based DTB selection, so it coexists cleanly with the other RK3326 "b" boards without any impact on them.

Working: display, audio, RTL8188EUS WiFi dongle, USB peripherals (keyboard/storage etc. via the OTG port - now correctly powers what's plugged in), all buttons (A/B swap fixed), battery status LEDs, battery/fuel-gauge reporting, standalone-emulator input controls across all bundled apps.

Last remaining TO-DO: wake-from-sleep still doesn't work correctly - proper suspend-to-memory needs further investigation. A temporary freeze (s2idle) workaround is included so sleep/resume is at least usable in the meantime. This is the only thing standing between this and calling it fully complete.

Release with SD card image + update tar: https://github.com/Jacob-Matthew-Cook/distribution/releases/tag/pixel2-alpha-1.0

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Update: fixed a regression introduced by the USB vbus-supply fix from the previous update — the port was always self-powering in host mode and never auto-negotiating charging/device mode when plugged into a charger or PC (only manually forcing gadget mode worked).

Root cause: the shared RK3326 usb-role-switch default ("host", from 022-usb-role-switch.patch) had been harmless on every other RK3326 board since none of them wire vbus-supply - defaulting to host never actually drove VBUS. Once we wired it for peripheral power, "default host" became electrically real for the first time, permanently self-sourcing 5V and starving dwc2's ID/VBUS-valid detection of a clean transition to notice. The vendor kernel never sets a default role at all here, relying purely on hardware ID/VBUS detection - dropped the forced default for this board only (shared patch and every other RK3326 board untouched).

Release assets updated in place: https://github.com/Jacob-Matthew-Cook/distribution/releases/tag/pixel2-alpha-1.0

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Wrapping up this development pass — Alpha 1.1 to follow.

We're done with our current round of work on the Pixel2 and would like to get this merged before starting on more complex features, so that work can build on a merged baseline rather than an ever-growing fork branch.

Since the last update, we've added:

  • More robust panel dimming: on many units, the display would turn off entirely if brightness was set to the minimum, since the panel can't sustain visible output below a certain PWM duty cycle. The brightness floor has been raised accordingly, to ~17/255 instead of ~12/255.
  • A more robust battery/power LED display: solid red below 10%, and 1/2/3/4 solid green LEDs for the 25/50/75/100% tiers respectively, plus a distinct charging pattern.
  • The LEDs now flash all together to signal when the device is stuck in USB "Host" mode following insertion of a peripheral - this is the state where the port self-sources 5V and can't sense external power, so this gives a clear visual indicator (and prompts the user to reset the device) instead of silently failing to charge.

Known "nice to haves" for follow-up work (not blocking this merge):

  1. More robust USB-OTG state transitions - the port can currently get stuck in either a "Host" or "Peripheral" configuration depending on whether a dongle or a wall charger was plugged in most recently, requiring a device reset to recover.
  2. Fix PWM motor (rumble) initialization - if enabled in the devicetree, the motor is always on until the driver later activates/deactivates it at some point; root cause still open despite several fix attempts.
  3. Properly configure Suspend-to-Memory sleep mode - freeze (s2idle) remains the working interim mode in the meantime.

Let us know if there's anything else needed before merge.

@Jacob-Matthew-Cook
Jacob-Matthew-Cook force-pushed the gkd-pixel2-support branch 2 times, most recently from 6b80a7a to 6fee235 Compare July 13, 2026 21:47
@Jacob-Matthew-Cook

Jacob-Matthew-Cook commented Jul 13, 2026

Copy link
Copy Markdown
Author

Update on the rumble motor: the long-standing "always on from boot" issue is partially fixed.

Root cause found via live-hardware register testing: the vendor-extracted rk3326s-gkd-pixel2.dtsi uses normal PWM polarity for the vibrator channel, but this board's dts used inverted polarity - chosen originally to match the channel's already-stuck-on boot state, which was backwards (that boot state was the bug, not the intended spec). Matching the vendor's real polarity was necessary but not sufficient on its own.

The remaining piece is fixed with a small rocknix-joypad patch that forces a genuine mid-duty "kick" at probe time before disabling the channel - this reliably clears the stuck-on-boot state on real hardware.

EDIT: I have properly fixed it. For some reason on the Pixel2, pwm0 was in the joypad, when it is supposed to be managed by pwm-vibra. I still need a patch at boot to initialize the pwm0 on the Pixel2 to off, but by using this module, we have made it appear almost immediately in the boot process.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Alpha 1.2 - PWM rumble mostly resolved.

Following up on the rumble motor issue: it's now fixed via a small kernel patch (030-pwm-vibra-kick-stuck-boot-state.patch) rather than anything in the joypad driver. Root cause was twofold:

  1. The channel was wired through rocknix-singleadc-joypad's own rumble handling, an out-of-tree loadable kernel module that doesn't probe until ~10s into boot. Moved it to the mainline pwm-vibrator driver instead (already built into the kernel image, and already used by sibling RK3326 boards like rg351m/rg351v), which probes during early kernel init (~0.65s, matching stock firmware).
  2. That alone wasn't sufficient: the channel can power up already latched in a stuck-on state that a plain disable never clears. The new kernel patch kicks it at probe time - ramping duty from 90% down to 0% over 250ms before disabling - forcing the first real hardware transition automatically, before userspace ever sees the device. Scoped to soc_is_px30s() so it only affects this silicon variant; every other board sharing this driver keeps the original, unmodified behavior.

Uploading this now as Alpha 1.2.

Remaining known issues:

  1. Suspend-to-memory sleep still doesn't wake correctly - freeze (s2idle) remains the working interim mode.
  2. USB-OTG state transitions aren't fully robust - the port can get stuck in either a "Host" or "Peripheral" configuration depending on what was plugged in most recently, requiring a device reset to recover.

This is intended to be the last major Alpha release before this gets merged into mainline - further fixes for the two items above would ideally happen post-merge rather than continuing to grow this branch further.

@AveyondFly

Copy link
Copy Markdown

Alpha 1.2 - PWM rumble mostly resolved.

Following up on the rumble motor issue: it's now fixed via a small kernel patch (030-pwm-vibra-kick-stuck-boot-state.patch) rather than anything in the joypad driver. Root cause was twofold:

1. The channel was wired through `rocknix-singleadc-joypad`'s own rumble handling, an out-of-tree loadable kernel module that doesn't probe until ~10s into boot. Moved it to the mainline `pwm-vibrator` driver instead (already built into the kernel image, and already used by sibling RK3326 boards like `rg351m`/`rg351v`), which probes during early kernel init (~0.65s, matching stock firmware).

2. That alone wasn't sufficient: the channel can power up already latched in a stuck-on state that a plain disable never clears. The new kernel patch kicks it at probe time - ramping duty from 90% down to 0% over 250ms before disabling - forcing the first real hardware transition automatically, before userspace ever sees the device. Scoped to `soc_is_px30s()` so it only affects this silicon variant; every other board sharing this driver keeps the original, unmodified behavior.

Uploading this now as Alpha 1.2.

Remaining known issues:

1. Suspend-to-memory sleep still doesn't wake correctly - `freeze` (s2idle) remains the working interim mode.

2. USB-OTG state transitions aren't fully robust - the port can get stuck in either a "Host" or "Peripheral" configuration depending on what was plugged in most recently, requiring a device reset to recover.

This is intended to be the last major Alpha release before this gets merged into mainline - further fixes for the two items above would ideally happen post-merge rather than continuing to grow this branch further.

Great work! Some comments.

Regarding the vibration, I assume just adding things like that pwm and set pwm0 as okay should be fine. Nothing special is needed. At least from my 5.10 port which uses the same driver.

My LLVM also added a special patch for otg switch, maybe as reference.

And spleep suspend usually strong related to u-boot.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Wake-from-sleep is fixed. Root cause: the earlier suspend-mode-config port only forwarded rockchip,sleep-mode-config/wakeup-config once, at probe time. The vendor driver also sends LINUX_PM_STATE (which suspend state Linux is actually entering) via the same SIP call on every suspend attempt, from a dev_pm_ops.prepare hook - that call was missing entirely from the earlier port. Added it (031-px30s-suspend-wakeup-config.patch), and the device now wakes correctly from "mem" suspend. Confirmed on real hardware.

New follow-up issue found in the process: the system crashes a few seconds after waking. Investigating separately - will update here once resolved.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Quick update: found and fixed a real bug where the GKD Pixel2 (PX30S) was running its CPU/GPU on generic PX30 voltage tables, overvolting the silicon at every frequency step. Corrected OPP tables are in now, with vendor's full frequency range and a safety margin validated on hardware.

Still actively working on getting resume-from-suspend fully stable - wakes correctly now, but a hang a few seconds after resume remains under investigation.

@Jacob-Matthew-Cook

Jacob-Matthew-Cook commented Jul 15, 2026

Copy link
Copy Markdown
Author

Alpha 1.3 (Final) is complete - proper suspend-to-memory now SEMI working

Deep suspend/resume (real mem sleep, not just freeze/s2idle) now works consistently on real hardware. Root cause was two compounding bugs, both fixed:

  1. A GPU clock double-management bug between genpd's automatic clock tracking and the mali_kbase driver's own direct clock management, confirmed via live dmesg to cause "Enabling unprepared clk_gpu" / -ESHUTDOWN during real resume.
  2. Incorrect CPU/GPU OPP table voltages - this board's real vendor firmware uses the generic PX30 voltage tables, not the lower-voltage PX30S-suffixed ones an earlier port assumed, which was silently undervolting the CPU/GPU at their higher operating points.

The freeze-as-default-suspend-mode workaround has been removed now that deep suspend is reliable.

The only remaining known issue is out of scope for this PR: USB-OTG state transitions aren't fully robust - the port can get stuck in either a "Host" or "Peripheral" configuration depending on what was plugged in most recently, requiring a device reset to recover. Left for follow-up work post-merge.

Alpha 1.3 build artifacts (flashable image + update tar, with checksums): https://github.com/Jacob-Matthew-Cook/distribution/releases/tag/pixel2-alpha-1.3

@labac-dev

Copy link
Copy Markdown

@Jacob-Matthew-Cook I tested the Alpha 1.3 build on real hardware. The first boot was completely normal. However, after changing the language and timezone in the Settings and performing a restart, the device gets stuck on a black screen (the backlight is on, but there is no display) right after the boot logo.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

@Jacob-Matthew-Cook I tested the Alpha 1.3 build on real hardware. The first boot was completely normal. However, after changing the language and timezone in the Settings and performing a restart, the device gets stuck on a black screen (the backlight is on, but there is no display) right after the boot logo.

You're right, I am noticing sometimes that the screen does this sometimes after restarting. Thanks for testing. I am not sure if this is some issue with the panel timing or the device internals not fully resetting and being in a weird state (the latter is my current theory). I have also updated the Alpha with our findings today that deep suspend does not work completely, and have since readded the necessary quirk file for freeze to be the default.

If your screen is completely black, fully power cycle the device, or suspend-then-resume. It is not the most elegant solution, but we will want to troubleshoot the display drivers a bit more as well. @stolen I think this branch is ready for merge into nightly, if you're up for it?

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Spent some time auditing every PX30S-specific difference in the vendor BSP kernel (rockchip-linux/kernel, checked against both develop-4.4 and develop-5.10 for consistency) against what's already ported here, focused on DVFS/clocks/power.

Found and fixed one real gap: clk-px30.c's DDR PHY/OTP clock topology. Every other PX30S subsystem difference already has a soc_is_px30s()-gated patch in this series (023-025, 027-028), but the clock driver itself was still unconditionally using vanilla PX30's topology on PX30S boards. On PX30S, clk_ddrphy1x is its own independently muxed/divided clock rather than a fixed /4 of a clk_ddrphy4x parent, and clk_stdby_2wrap runs at a quarter of clk_ddrphy1x's rate instead of the same rate as it - a genuinely different clock relationship, not just a rename. Ported from vendor commit f89c8138cab3 as 033-px30s-ddrphy-clock-topology.patch. This hasn't been hardware-tested yet - it applies cleanly and compiles, but I haven't confirmed it actually changes the RAM-corruption behavior we've been chasing separately.

Everything else already covered, for completeness:

  • GPU power domain (024) - already keeps pd_gpu from ever powering off on PX30S, plus skips genpd's generic clock tracking so it doesn't fight with mali_kbase's own clock management. Confirmed via dmesg this is real and necessary, but on its own it's not sufficient to explain the corruption either - it's been compiled into every image tested so far.
  • io-domain.c (pmuio1 1.8V/3.3V switch, 027), rockchip-otp.c (read protocol, 032), rockchip_thermal.c (calibration table, 023), MIPI/USB2 PHY tuning (025, 028) - all already ported, none of these are DVFS-relevant beyond thermal calibration accuracy.

One architectural difference with no fix planned, just noting it: the vendor BSP has an entire subsystem (rockchip_opp_select.c, ~2100 lines, no mainline equivalent) that measures each physical chip's actual speed/leakage at boot via a PVTM ring-oscillator hardware block, temperature-compensates it, and picks a voltage bin calibrated to that specific unit. Confirmed active (not dead code) on this exact board's vendor devicetree, for both CPU and GPU. Mainline/ROCKNIX has none of this and always uses the single safest/highest-margin voltage bin for every unit. Unlikely to be a source of instability (it errs toward more margin, not less), but worth knowing this device's real silicon was individually calibrated by vendor firmware in a way we don't replicate.

@Jacob-Matthew-Cook

Jacob-Matthew-Cook commented Jul 16, 2026

Copy link
Copy Markdown
Author

@Jacob-Matthew-Cook I tested the Alpha 1.3 build on real hardware. The first boot was completely normal. However, after changing the language and timezone in the Settings and performing a restart, the device gets stuck on a black screen (the backlight is on, but there is no display) right after the boot logo.

I think I found the issue. There were a few errors in the panel dts description provided by @stolen, which have been fixed. I am updating Alpha 1.3 again in-place, but I am placing the vendor CPU and GPU opp tables in place (which can go to much lower power levels).

The sleep issues seem to be some combination of voltage changing much too quickly for this chip following resume, which we haven't fully characterized. I will need to keep testing @labac-dev, but the in-place Alpha 1.3 update should fix the display.

I highly encourage people to try debugging and testing the system via SSH over USB (or UART, if they want to get panics closer the the kernel failure). Given the RAM can corrupt during voltage transitions, it is unlikely the UART can catch the exact cause of failure.

If the system is left in performance mode or powersave mode, suspend to memory works. Do be careful, as ondemand and schedutil can cause the system to crash following resume.

Lastly, you may notice overclocking from stock settings may reboot your device. If that is the case, you should enable CPU performance mode first, and THEN set the CPU overclock.

@aenertia

Copy link
Copy Markdown
Contributor

Did you implement TEO/Uclamp stack with Schedutil?s - I had to adjust the polling rate down of the freq to be stable for the xf40h and rk3566 work I was doing a few months to get stable results, the defaults poll too aggressively and trigger freq changes which cause crashes . schedutils is probably the right govt in general once dialed in with systemd and TEO, but you need the whole uclamp/teo/cgroupv2 stack setup properly. There should be some discussion on this buried in github from a few months back.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

Did you implement TEO/Uclamp stack with Schedutil?s - I had to adjust the polling rate down of the freq to be stable for the xf40h and rk3566 work I was doing a few months to get stable results, the defaults poll too aggressively and trigger freq changes which cause crashes . schedutils is probably the right govt in general once dialed in with systemd and TEO, but you need the whole uclamp/teo/cgroupv2 stack setup properly. There should be some discussion on this buried in github from a few months back.

I don't think I have TEO/Uclamp stack implemented. Also, the issue occurs with either Schedutil or Ondemand. I'll look into your points. How critical is BL32 in your opinion?

PR#2360 is eerily similar to what we are working through right now... very interesting:
#2360

@aenertia

aenertia commented Jul 17, 2026 via email

Copy link
Copy Markdown
Contributor

@Jacob-Matthew-Cook

Jacob-Matthew-Cook commented Jul 17, 2026

Copy link
Copy Markdown
Author

Yes - the DMC freq selection was part of what I took further and implemented; it's not really as relevant on the rk3326 as on the 3566. bl32 isn't relevant it was a red herring IIRC. There is probably some more advanced branches in my github repos.>

Interesting. Just a heads up, the Pixel2 uses the RK3326-S variant (not the RK3326 base), which is somewhat modernized and has overall better power performance (with a few newer features). It can support faster RAM if I am not mistaken, and I think it may have some similar structural aspects to newer SoCs from Rockchip. I'll keep looking into this as I have time.

If you have any technical input regarding DVFS on the Rockchip SoCs, that would be appreciated.

@Jacob-Matthew-Cook Jacob-Matthew-Cook changed the title RK3326: add GKD Pixel 2 (subdevice c) support RK3326: add GKD Pixel 2 (Device b) support Jul 17, 2026
@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

@stolen I think we are ready to merge, if you are? I will need to solve the DRAM corruption in a separate PR, as discussed on Discord. Thank you for all the time and help!

@spycat88

Copy link
Copy Markdown
Contributor

@stolen I think we are ready to merge, if you are? I will need to solve the DRAM corruption in a separate PR, as discussed on Discord. Thank you for all the time and help!

Rebase your PR and remove the merge commits.

- b33da5dd27 Merge branch 'next' into gkd-pixel2-support
- a7de6fce81 Merge branch 'next' into gkd-pixel2-support

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

@stolen I think we are ready to merge, if you are? I will need to solve the DRAM corruption in a separate PR, as discussed on Discord. Thank you for all the time and help!

Rebase your PR and remove the merge commits.

- b33da5dd27 Merge branch 'next' into gkd-pixel2-support
- a7de6fce81 Merge branch 'next' into gkd-pixel2-support

I think I fixed it now? If there are other issues please let me know. I'm still a bit new to this, so thanks for walking me through this.

@stolen

stolen commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@spycat88 I'm not familiar with github PR features. Can we squash the entire PR while accepting it?

@spycat88

Copy link
Copy Markdown
Contributor

@spycat88 I'm not familiar with github PR features. Can we squash the entire PR while accepting it?

You can, but we disabled it, is that something you really want to do? The PR author can also do this themselves and force push the change to the PR.

@Jacob-Matthew-Cook

Copy link
Copy Markdown
Author

@spycat88 I'm not familiar with github PR features. Can we squash the entire PR while accepting it?

You can, but we disabled it, is that something you really want to do? The PR author can also do this themselves and force push the change to the PR.

I can squash the commit history, I am fine with that.

Adds a mainline/ROCKNIX-style board port for the GameKiddy GKD Pixel2
(RK3326S/PX30S), reconstructed from the vendor-extracted devicetree on
top of the shared rk3326.dtsi, alongside the U-Boot and kernel-side
fixes needed for it to work reliably.

Devicetree / display:
- Board dts with panel, input, LED, and OPP wiring specific to this
  board. Panel uses the rocknix,generic-dsi driver (already used by
  other RK3326 boards) with an init sequence reconstructed from the
  vendor-extracted panel-init-sequence and trimmed to the single
  validated 480x640@60Hz mode.
- GPIO LED control for the five battery-indicator LEDs.
- Board-local CPU/GPU/DMC OPP tables using this board's own real,
  vendor-validated voltages, confirmed against the vendor's own
  decompiled devicetree (this board's real firmware uses the generic
  PX30 tables rather than lower-voltage PX30S-suffixed ones some other
  porting attempts assume). The 1512MHz CPU turbo step is disabled -
  it can trigger a full device reset on its own, confirmed by direct
  reproduction.

Kernel (PX30S-specific fixes ported from the vendor BSP kernel, each
gated on soc_is_px30s() so other RK3326/PX30 boards are unaffected):
- TSADC support, GPU power-domain workaround (a clock double-
  management bug between genpd's automatic GENPD_FLAG_PM_CLK tracking
  and mali_kbase's own direct clock management, confirmed via live
  dmesg to cause "Enabling unprepared clk_gpu" / -ESHUTDOWN during real
  resume - genpd no longer tracks the GPU domain's clock on PX30S),
  DSI D-PHY timing table, cpuinfo SoC detection, IO-domain pmuio1
  fix, USB2 PHY tuning, OTP active/standby read protocol (mainline
  otherwise always uses the wrong, plain-PX30 read path), CPU/GPU
  wake/suspend-mode SIP configuration for firmware-controlled deep
  suspend states, and DDR PHY/OTP clock topology (PX30S's
  clk_ddrphy1x is independently muxed/divided rather than a fixed /4
  of clk_ddrphy4x, and clk_stdby_2wrap runs at a quarter of
  clk_ddrphy1x's rate rather than the same rate).
- Rumble via the mainline pwm-vibrator driver, with a fix for a
  stuck-on-boot PWM state.

With the suspend/resume and OPP fixes above, deep suspend (not just
s2idle) works on this hardware. However, real hardware testing found
a separate, unresolved bug: if a dynamic CPU governor (schedutil/
ondemand) is active after resume, the system reliably crashes within
seconds (confirmed memory corruption - a real page-table translation
fault), needing both a CPU frequency/voltage transition and concurrent
memory-management activity to manifest. Static governors and a fresh
boot are unaffected - only post-resume with a dynamic governor is.
Until this is root-caused, this board defaults to "freeze" (s2idle)
suspend instead of deep suspend, sidestepping the corruption path
rather than fixing it (same mechanism RK3576 uses for the same class
of issue).

U-Boot:
- k36-clone-with-emmc-rk3326 support, OTP support removed (not needed
  once the kernel-side OTP read protocol above works), and the
  ADC-based download-key false-trigger disabled via Kconfig (this
  board has no analog stick, so the floating SARADC channel otherwise
  reads as "key pressed").

Known open issues, not resolved by this PR:
- Rumble motor can get stuck on (PWM channel boots enabled/inverted/
  duty==period) in some cases.
- The post-resume dynamic-governor memory corruption described above;
  DMC/DRAM frequency-scaling work to investigate it is being tracked
  separately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@stolen
stolen added this pull request to the merge queue Jul 17, 2026
Merged via the queue into ROCKNIX:next with commit 25398f7 Jul 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants